﻿using LPBToolsLib;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using System.Threading;
using static System.Collections.Specialized.BitVector32;

namespace StationSer
{

    /// <summary>
    /// 数据交换数据帧对象
    /// 头
    ///  00 01 00 01 00 18 00 01   A0 01 00 01 00 00 00 01 B0 01 00 01 00 00 00 01
    ///       Header             |      StationID
    ///  [00-01]:       TranID;传输ID，保证正确传输及应答
    ///  [02-03]:       ProtrolID;协议ID，默认1
    ///  [04-05]:       DatasLen;包括头的全部数据字节长度
    ///  [06-07]:       ResTranID;下位执行完命令后，将执行命令传输ID放置在此处，否则为0
    ///  [08-23]:       StaID;工作站16位长度ID
    ///  
    ///  扩展数据：DEXOpts_Byte      长度0-N字节
    ///  
    /// </summary>
    public class DExPacket : ContainerBase
    {

        private List<byte> tDatas { get; } = new List<byte>();
        /// <summary>
        /// 创建新的对象
        /// </summary>
        public DExPacket()
        {
            tDatas.Clear();
            tDatas.AddRange(new byte[8]);
        }
        /// <summary>
        /// 将收到的数据流解析为对象
        /// </summary>
        /// <param name="tSourDatas"></param>
        public bool setPacketDatas(byte[] tSourDatas)
        {
            tDatas.Clear();
            tDatas.AddRange(tSourDatas);
            return isReady;
        }

        public byte[] getPacketDatas()
        {
            // TranID [00:01]
            // ProtrolID [02:03];
            // DatasLen [04:05]
            // ResTranID [06:07]
            // StaID [08:24]
            tDatas[4] = (byte)((tDatas.Count >> 8) & 0xFF);
            tDatas[5] = (byte)((tDatas.Count >> 0) & 0xFF);
            return tDatas.ToArray();
        }

        public static DExPacket getSerExPacket(byte[] tSourDatas)
        {
            DExPacket tResult = new DExPacket();
            if (tResult.setPacketDatas(tSourDatas))
                return tResult;
            return null;
        }

        #region 数据帧头
        /// <summary>
        /// 传输ID，保证正确传输及应答
        /// </summary>
        public int TranID
        {
            get
            {
                if (tDatas.Count >= 8)
                {
                    int iTmp = tDatas[0];
                    iTmp = (iTmp << 8) | tDatas[1];
                    return iTmp;
                }
                return 0;
            }
            set // 仅做测试使用
            {
                if (tDatas.Count >= 8)
                {
                    tDatas[0] = (byte)((value >> 8) & 0xFF);
                    tDatas[1] = (byte)((value >> 0) & 0xFF);
                }
            }
        }

        /// <summary>
        /// 协议ID，默认1
        /// 作为PLC类型
        /// </summary>
        public int ProtrolID
        {
            get
            {
                if (tDatas.Count >= 8)
                {
                    int iTmp = tDatas[2];
                    iTmp = (iTmp << 8) | tDatas[3];
                    return iTmp;
                }
                return 0;
            }
            set // 仅做测试使用
            {
                if (tDatas.Count >= 8)
                {
                    tDatas[2] = (byte)((value >> 8) & 0xFF);
                    tDatas[3] = (byte)((value >> 0) & 0xFF);
                }
            }
        }
        /// <summary>
        /// 包括头的全部数据字节长度
        /// </summary>
        public int DatasLen
        {
            get
            {
                if (tDatas.Count >= 8)
                {
                    int iTmp = tDatas[4];
                    iTmp = (iTmp << 8) | tDatas[5];
                    return iTmp;
                }
                return -1;
            }
        }
        /// <summary>
        /// 下位执行完命令后，将执行命令传输ID放置在此处，否则为0
        /// </summary>
        public int ResTranID
        {
            get
            {
                if (tDatas.Count >= 8)
                {
                    int iTmp = tDatas[6];
                    iTmp = (iTmp << 8) | tDatas[7];
                    return iTmp;
                }
                return -1;
            }
            set // 仅做测试使用
            {
                if (tDatas.Count >= 8)
                {
                    tDatas[6] = (byte)((value >> 8) & 0xFF);
                    tDatas[7] = (byte)((value >> 0) & 0xFF);
                }
            }
        }

        #endregion

        /// <summary>
        /// 检查数据创建的命令是否有效
        /// </summary>
        public bool isReady
        {
            get
            {
                return (TranID != 0) && (ProtrolID != 0) && (DatasLen == tDatas.Count)
                    && (ResTranID >= 0);
            }
        }

        /// <summary>
        /// 工作站ID
        /// 返回应答时该段无效
        /// </summary>
        public string StatioinID
        {
            get
            {
                byte[] tID = new byte[24 - 8];
                if (tDatas.Count >= 24)
                    for (int i = 0; i < 24 - 8; i++)
                        tID[i] = tDatas[8 + i];
                return BaseTools.BytesToStrHex(tID).Replace(" ", string.Empty).ToUpper();
            }
            set // 仅做测试使用
            {
                while (tDatas.Count > 8)
                    tDatas.RemoveAt(tDatas.Count - 1);
                tDatas[4] = 0;
                tDatas[5] = 8;
                if ((tDatas.Count >= 8)
                    && (!string.IsNullOrEmpty(value))
                    && (value.Length == 16))
                {
                    tDatas.AddRange(Encoding.UTF8.GetBytes(value));
                    tDatas[4] = (byte)((tDatas.Count >> 8) & 0xFF);
                    tDatas[5] = (byte)((tDatas.Count >> 0) & 0xFF);
                }
            }
        }
        /// <summary>
        /// 扩展数据
        /// 如果是根据收到的数据流解析，此段于StaID重复
        /// 仅在创建应答数据帧时可以写入
        /// </summary>
        public byte[] DEXOptDatas
        {
            get
            {
                if (isReady && (1 < (tDatas.Count - 24)))
                {
                    byte[] tBuf = new byte[tDatas.Count - 24];
                    for (int i = 0; i < tDatas.Count - 24; i++)
                        tBuf[i] = tDatas[i + 24];
                    return tBuf;
                }
                return null;
            }
        }

        /// <summary>
        /// 向节点追加Option数据，仅作为测试工具使用
        /// </summary>
        /// <param name="tOptDatas"></param>
        public void AppendOptDatas(byte[] tOptDatas)
        {
            if (isReady && (tDatas.Count >= 24)
                &&(tOptDatas!=null)&&(tOptDatas.Length>0))
            {
                tDatas.AddRange(tOptDatas);
                tDatas[4] = (byte)((tDatas.Count >> 8) & 0xFF);
                tDatas[5] = (byte)((tDatas.Count >> 0) & 0xFF);
            }
        }

        /*
        /// <summary>
        /// 根据数据包对象，生成传输的数据流，仅做参考
        /// </summary>
        /// <returns></returns>
        public byte[] getPacketDatasX()
        {
            List<byte> tResult = new List<byte>();
            if (isReady)
            {
                if (TranID < 0x8000)
                    TranID++;
                else
                    TranID = 1;
                // TranID [00:01]
                tResult.Add((byte)((TranID >> 8) & 0xFF));
                tResult.Add((byte)((TranID >> 0) & 0xFF));

                // ProtrolID [02:03]
                tResult.Add((byte)((ProtrolID >> 8) & 0xFF));
                tResult.Add((byte)((ProtrolID >> 0) & 0xFF));

                // DatasLen [04:05]
                int iDLen = 24;
                tResult.Add((byte)((iDLen >> 8) & 0xFF));
                tResult.Add((byte)((iDLen >> 0) & 0xFF));

                // ResTranID [06:07]
                tResult.Add((byte)((ResTranID >> 8) & 0xFF));
                tResult.Add((byte)((ResTranID >> 0) & 0xFF));


                // StaID [08:24]
                string StaID = BaseLib.GUID16();
                tResult.AddRange(Encoding.UTF8.GetBytes(StaID));


                // OptCommand
                
                uint16_t iTmp = Opt_UploadRegValue_0x03;
                DExClient.TxBuf[24] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[25] = (iTmp >> 0) & 0xFF;

                // OptLen
                DExClient.TxBuf[26] = 0;
                DExClient.TxBuf[27] = 22;

                // RegStar
                DExClient.TxBuf[28] = 0;
                DExClient.TxBuf[29] = RegDef_Input;

                // RegCount
                DExClient.TxBuf[30] = 0;
                DExClient.TxBuf[31] = 7;

                // RegValue
                iTmp = SysRegisters_ReadRegWordEx(RegDef_Input);
                DExClient.TxBuf[32] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[33] = (iTmp >> 0) & 0xFF;
                iTmp = SysRegisters_ReadRegWordEx(RegDef_Output);
                DExClient.TxBuf[34] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[35] = (iTmp >> 0) & 0xFF;
                iTmp = SysRegisters_ReadRegWordEx(RegDef_RunType);
                DExClient.TxBuf[36] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[37] = (iTmp >> 0) & 0xFF;
                iTmp = SysRegisters_ReadRegWordEx(RegDef_MotorSleepping);
                DExClient.TxBuf[38] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[39] = (iTmp >> 0) & 0xFF;
                iTmp = SysRegisters_ReadRegWordEx(RegDef_MotorSleepLong);
                DExClient.TxBuf[40] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[41] = (iTmp >> 0) & 0xFF;
                iTmp = SysRegisters_ReadRegWordEx(RegDef_MotorPowerOnTick);
                DExClient.TxBuf[42] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[43] = (iTmp >> 0) & 0xFF;
                iTmp = SysRegisters_ReadRegWordEx(RegDef_MotorPowerOnLong);
                DExClient.TxBuf[44] = (iTmp >> 8) & 0xFF;
                DExClient.TxBuf[45] = (iTmp >> 0) & 0xFF;
                

            }
            return tResult.ToArray();
        }
*/
    }

    /// <summary>
    /// 通讯数据包的应答
    /// </summary>
    public class DExRespon
    {
        public bool isReady { get { return (TranID > 0) && (ProtrolID > 0); } }
        public int TranID { get; private set; } = 0;

        /// <summary>
        /// 协议ID，默认1
        /// 作为PLC类型
        /// </summary>
        public int ProtrolID { get; private set; } = 0;
        
        /// <summary>
        /// 下位执行完命令后，将执行命令传输ID放置在此处，否则为0
        /// </summary>
        public int ResTranID { get; private set; } = 0;
        /// <summary>
        /// 需要下达的命令数据
        /// </summary>
        private List<byte> ResCommandDatas { get; set; } = null;
        public void setOptDatas(int iResCommandTranID, byte[] tResCommandDatas)
        {
            if ((iResCommandTranID > 0)
                && (tResCommandDatas != null) && (tResCommandDatas.Length > 0))
            {
                ResTranID = iResCommandTranID;
                ResCommandDatas = new List<byte>(tResCommandDatas);
            }
        }
        public DExRespon(DExPacket tSourPacket)
        {
            if (tSourPacket!=null)
            {
                TranID = tSourPacket.TranID;
                ProtrolID= tSourPacket.ProtrolID;
            }
        }
        public byte[] getResponDatas()
        {
            byte[] tPacketDatas = null;
            getResponDatas(out tPacketDatas);
            return tPacketDatas;
        }
        /// <summary>
        /// 提取对下位终端机的应答数据，可能包括下达的命令信息
        /// 应答不包括工作站的ID，因此数据头长度为8
        /// </summary>
        /// <param name="tPacketDatas"></param>
        /// <returns></returns>
        public bool getResponDatas(out byte[] tPacketDatas)
        {
            tPacketDatas = null;
            if (isReady)
            {
                List<byte> tBuf = new List<byte>();

                tBuf.Add((byte)((TranID >> 8) & 0xFF));
                tBuf.Add((byte)((TranID >> 0) & 0xFF));

                tBuf.Add((byte)((ProtrolID >> 8) & 0xFF));
                tBuf.Add((byte)((ProtrolID >> 0) & 0xFF));

                tBuf.Add(0);
                tBuf.Add(0);

                // 是否存在应答数据
                if ((ResTranID > 0) && (ResCommandDatas != null) && (ResCommandDatas.Count > 0))
                {
                    // 有应答数据，将应答数据写入数据流
                    // 应答的数据包ID
                    tBuf.Add((byte)((ResTranID >> 8) & 0xFF));
                    tBuf.Add((byte)((ResTranID >> 0) & 0xFF));
                    // 应答的数据
                    for (int i = 0; i < ResCommandDatas.Count; i++)
                        tBuf.Add(ResCommandDatas[i]);
                }
                else
                {
                    // 没有应答
                    tBuf.Add(0);
                    tBuf.Add(0);
                }
                // 根据实际数据长度，设置对应位置数据
                tBuf[4] = (byte)((tBuf.Count >> 8) & 0xFF);
                tBuf[5] = (byte)((tBuf.Count >> 0) & 0xFF);

                // 应答不包括工作站的ID，因此数据头长度为8
                if (tBuf.Count >= 8)
                {
                    // 记录应答次数
                    tPacketDatas = tBuf.ToArray();
                    return true;
                }
            }
            return false;
        }
    }

    public interface IFDExCommunicater
    {
        string StationID { get; }
        int CommandTOutMs { get; set; }
        // event OnDExRecOption OnRecOptioin;
        DExRespon receNewPacket(DExPacket tRecPacket);
        void setStation(IFStation tStation);
        IFStation Station { get; }
        bool IsTimeOut { get; }
    }

    public delegate void OnDExRecOption(IFDExCommunicater sender, byte[] OptionDatas, out byte[] ResponOpt);
    /// <summary>
    /// 数据流通讯控制器
    /// 用于管理数据包的通讯，控制应答的发送及确认
    /// 
    /// </summary>
    public class DExPacketCommunicate: IFDExCommunicater
    {
        public Recorder_Error SysErr { get; set; } = null;
        public Recorder_RunLog RunLog { get; set; } = null;
        public string StationID { get; protected set; } = "";
        public int CommandTOutMs { get; set; } = 10000;

        public bool IsTimeOut { get { return (DateTime.Now - LastPacketTime).TotalMilliseconds > (CommandTOutMs * 3); } }
        #region  成员属性
        public int LastTranID { get; private set; } = 0;
        private DateTime LastPacketTime { get; set; } = DateTime.Now;
        private DateTime LastCommandTime { get; set; } = DateTime.Now;
        /// <summary>
        /// 需要下达的命令数据
        /// </summary>
        private List<byte> ResCommandDatas { get; } = new List<byte>();
        /// <summary>
        /// 下达命令的传送ID，如果有命令则创建，使用随机数
        /// </summary>
        private int ResCommandTID { get; set; } = 0;

        private Random tRD = new Random();
        #endregion

        #region 处理收到的数据包
        /// <summary>
        /// 与Station对象的接口，用于处理收到的Option
        /// </summary>
        protected event OnDExRecOption OnRecOptioin;
        /// <summary>
        /// 处理收到的数据包
        /// 根据传输ID及应答ID判断应答
        /// </summary>
        /// <param name="tRecPacket">收到的数据包</param>
        /// <returns>返回的应答数据对象</returns>
        public DExRespon receNewPacket(DExPacket tRecPacket)
        {
            DExRespon tRelayPacket = null;
            // 确认上次通讯是否有效
            if ((LastTranID != 0) && ((LastPacketTime - DateTime.Now).TotalMilliseconds > CommandTOutMs))
            {
                LastTranID = 0;
            }
            if ((ResCommandTID != 0) && ((LastCommandTime - DateTime.Now).TotalMilliseconds > CommandTOutMs))
            {
                ResCommandTID = 0;
                ResCommandTID = 0;
                ResCommandDatas.Clear();
            }

            if (tRecPacket != null)
            {
                tRelayPacket = new DExRespon(tRecPacket);
                // 确认此次通讯是否重复
                if ((LastTranID != 0) && (tRecPacket.TranID == LastTranID))
                {

                }
                if (tRecPacket.TranID != LastTranID)
                {
                    if ((ResCommandTID == tRecPacket.ResTranID) && (ResCommandTID != 0))
                    { // 上次传输的命令已经确认执行，清理记录
                        ResCommandTID = 0;
                        ResCommandDatas.Clear();
                    }
                    tOptionDatas = tRecPacket.DEXOptDatas;
                    if (tOptionDatas != null)
                    { // 将命令处理功能放入线程，避免通讯阻塞
                        Thread tRunWithOption = new Thread(thdMainRun);
                        tRunWithOption.Start();
                    }
                }
                // 为重复数据包，则返回
                if (ResCommandTID > 0)
                    tRelayPacket.setOptDatas(ResCommandTID, ResCommandDatas.ToArray());
                LastPacketTime = DateTime.Now;

            }
            return tRelayPacket;
        }

        private byte[] tOptionDatas { get; set; } = null;

        private void thdMainRun()
        {
            if (tOptionDatas!=null)
            {
                try
                {
                    byte[] tResponDatas = null;
                    if (Station == null)
                    {
                        // 如果工作站尚未创建，根据上传数据创建工作站
                        DExOptionBase tDOption = new DExOptionBase(tOptionDatas);
                        if ((tDOption != null) && tDOption.OptReady)
                        {
                            PLCStation tStation = new PLCStation(StationID, tDOption);
                            tStation.SysErr = SysErr;
                            tStation.RunLog = RunLog;
                            // 将刚创建的工作站对象，设置为接口目标工作站对象
                            setStation(tStation);
                        }
                    }
                    // 调用接口处理收到的命令
                    if (Station != null)
                        Station.RecOption(this, tOptionDatas, out tResponDatas);
                    else
                        OnRecOptioin?.Invoke(this, tOptionDatas, out tResponDatas);

                    if ((tResponDatas != null) && (tResponDatas.Length > 0))
                    { // 命令处理完成后，需要下达命令
                        ResCommandDatas.Clear();
                        LastCommandTime = DateTime.Now;
                        ResCommandDatas.AddRange(tResponDatas);
                        ResCommandTID = tRD.Next(1, 0x10000);
                    }
                }
                catch(Exception ex)
                {
                    SysErr?.setRecord(ex);
                }
                finally
                {
                    tOptionDatas = null;
                }
            }
        }
        #endregion

        public IFStation Station { get; private set; } = null;
        public void setStation(IFStation tStation )
        {
            Station = tStation;
            if (tStation != null)
            {
                // StatioinID 必须在创建时初始化，此处代码作废
                // StationID = tStation.StationID;
                // tStation.StationID = StationID;
                // 设置事件接口，将收到的命令传输到目标工作站对象进行处理
                OnRecOptioin = tStation.RecOption;
            }
        }

        public DExPacketCommunicate(string tDestStationID, IFStation tStation = null)
        {
            StationID = tDestStationID;
            setStation(tStation);
        }

    }
}
